import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.ArrayList;

/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: Dec 25, 2009
 * Time: 9:33:26 AM
 * To change this template use File | Settings | File Templates.
 */
public class Parser {

    /*input file*/
    private static final String INPUT_FILE_NAME = "input.in";

    /*output file*/
    private static final String OUTPUT_FILE_NAME = "syntaxer.out";

    /*tokens*/
    public static final String ATTRIBUTEDEFAULT = "ATTRIBUTEDEFAULT: ";
    public static final String CONTENTSDEFAULT = "CONTENTSDEFAULT: ";
    public static final String COMBINE = "_";
    public static final String SCHEMA = "SCHEMA: ";
    public static final String RULE = "RULE: ";
    public static final String DECLARATION = "DECLARATION: ";
    public static final String ATTRIBUTEDECL = "ATTRIBUTEDECL: ";
    public static final String BOOLEXP = "BOOLEXP: ";
    public static final String REGEXP = "REGEXP: ";
    public static final String DEFINITION = "DEFINITION: ";
    public static final String NORMALIZE = "NORMALIZE: ";
    public static final String UNIQUE = "UNIQUE: ";
    public static final String POINTER = "POINTER: ";
    public static final String FIELD = "FIELD: ";
    public static final String IMPORT = "IMPORT: ";
    public static final String PREFIX = "PREFIX: ";
    public static final String COMMENT = "COMMENT: ";
    public static final String DOCCUMENT = "DOCCUMENT: ";
    public static final String PROCESSING_INSTRUCTION = "PROCESSING_INSTRUCTION: ";
    public static final String ATTRIBUTEDEFAULT_OR_CONTENTSDEFAULT = "ATTRIBUTEDEFAULT_OR_CONTENTSDEFAULT: ";
    public static final String ATTRIBUTES = "ATTRIBUTES: ";

    /*ERROR*/
    public static final String ERROR = "ERROR: ";

    /*descriptions*/
    public static final String SUGGESTION1 = " -this parent tag has ";
    public static final String SUGGESTION2 = " which should not be there!";
    public static final String CAUSED_BY = "Caused by ";
    public static final String THIS_IS_NOT_A_VALID_DSD20_ELEMENT = "THIS IS NOT A VALID DSD 2.0 ELEMENT";

    /*indent*/
    static String INDENT = "  ";

    static FileWriter fos;
    static int currentLevel;
    public static ArrayList<String> ERRORCHILDREN;


    public static void main(String[] args) {
        ERRORCHILDREN = new ArrayList<String>();
        Reader filer;
        try {
            filer = new FileReader(INPUT_FILE_NAME);
            fos = new FileWriter(OUTPUT_FILE_NAME, true);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = null;
        InputSource is = new InputSource(filer);
        Document document;
        try {
            db = dbf.newDocumentBuilder();
            document = db.parse(is);
            preorderTravers(document.getDocumentElement()); /*traverse root*/
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            fos.write("\r\n");
            fos.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void preorderTravers(Node node) {
        scanParent(node);
        currentLevel++;
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            preorderTravers(list.item(i));
        }
        currentLevel--;
    }

    private static void scanParent(Node node) {
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            ////*SCHEMA*/
            if (("dsd").equals(node.getNodeName())) {
                if (scanChildNodesForSCHEMAParent(node)) {
                    print(SCHEMA + node.getNodeName());
                } else {
                    print(ERROR + SCHEMA + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                    clearErrorChildren();
                }
            }
            ////*RULE or DEFINITION*/
            else if (("declare").equals(node.getNodeName()) | ("require").equals(node.getNodeName()) | ("if").equals(node.getNodeName()) | ("rule").equals(node.getNodeName()) | ("unique").equals(node.getNodeName()) | ("pointer").equals(node.getNodeName())) {
                if (scanChildNodesForRULEParent(node)) {
                    print(RULE + node.getNodeName());
                } else if (("rule").equals(node.getNodeName())) {
                    if (scanChildNodesForDEFINITIONParent(node)) {
                        print(DEFINITION + node.getNodeName());
                    } else {
                        print(ERROR + RULE + COMBINE + DEFINITION + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                        clearErrorChildren();
                    }
                } else {
                    print(ERROR + RULE + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                    clearErrorChildren();
                }
            }
            ////*DEFINITION or REGEXP or BOOLEXP*/
            else if (("contenttype").equals(node.getNodeName()) | ("stringtype").equals(node.getNodeName()) | ("boolexp").equals(node.getNodeName())) {
                if (scanChildNodesForDEFINITIONParent(node)) {
                    print(DEFINITION + node.getNodeName());
                } else if (("contenttype").equals(node.getNodeName())) {
                    if (scanChildNodesForREGEXPParent(node)) {
                        print(REGEXP + node.getNodeName());
                    } else {
                        print(ERROR + DEFINITION + COMBINE + REGEXP + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                        clearErrorChildren();
                    }
                } else if (("stringtype").equals(node.getNodeName())) {
                    if (scanChildNodesForREGEXPParent(node)) {
                        print(REGEXP + node.getNodeName());
                    } else {
                        print(ERROR + DEFINITION + COMBINE + REGEXP + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                        clearErrorChildren();
                    }
                } else if (("boolexp").equals(node.getNodeName())) {
                    if (scanChildNodesForBOOLEXPParent(node)) {
                        print(BOOLEXP + node.getNodeName());
                    } else {
                        print(ERROR + DEFINITION + COMBINE + BOOLEXP + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                        clearErrorChildren();
                    }
                } else {
                    print(ERROR + DEFINITION + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                    clearErrorChildren();
                }
            }
            ////*DECLARATION*/
            else if (("attribute").equals(node.getNodeName()) | ("required").equals(node.getNodeName()) | ("contents").equals(node.getNodeName())) {
                if (scanChildNodesForDECLARATIONParent(node)) {
                    print(DECLARATION + node.getNodeName());
                } else if (("attribute").equals(node.getNodeName())) {
                    if (scanChildNodesForATTRIBUTEDECLParent(node)) {
                        print(ATTRIBUTEDECL + node.getNodeName());
                    } else {
                        print(ERROR + ATTRIBUTEDECL + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                        clearErrorChildren();
                    }
                } else if (("contents").equals(node.getNodeName())) {
                    if (scanChildNodesForBOOLEXPParent(node)) {
                        print(BOOLEXP + node.getNodeName());
                    } else {
                        print(ERROR + DECLARATION + COMBINE + BOOLEXP + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                        clearErrorChildren();
                    }
                } else {
                    print(ERROR + DECLARATION + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                    clearErrorChildren();
                }
            }
            /////*REGEXP*/
            else if (("sequence").equals(node.getNodeName()) | ("optional").equals(node.getNodeName()) | ("complement").equals(node.getNodeName()) | ("union").equals(node.getNodeName()) | ("intersection").equals(node.getNodeName()) | ("minus").equals(node.getNodeName()) | ("repeat").equals(node.getNodeName()) | ("string").equals(node.getNodeName()) | ("char").equals(node.getNodeName())) {
                if (scanChildNodesForREGEXPParent(node)) {
                    print(REGEXP + node.getNodeName());
                } else {
                    print(ERROR + REGEXP + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                    clearErrorChildren();
                }
            }
            ////*BOOLEXP*/
            else if (("and").equals(node.getNodeName()) | ("or").equals(node.getNodeName()) | ("not").equals(node.getNodeName()) | ("imply").equals(node.getNodeName()) | ("equiv").equals(node.getNodeName()) | ("one").equals(node.getNodeName()) | ("parent").equals(node.getNodeName()) | ("ancestor").equals(node.getNodeName()) | ("child").equals(node.getNodeName()) | ("descendant").equals(node.getNodeName()) | ("this").equals(node.getNodeName()) | ("element").equals(node.getNodeName())) {
                if (scanChildNodesForBOOLEXPParent(node)) {
                    print(BOOLEXP + node.getNodeName());
                } else {
                    print(ERROR + BOOLEXP + node.getNodeName() + SUGGESTION1 + getErrorChildren() + SUGGESTION2);
                    clearErrorChildren();
                }
            }
            /////*NORMALIZE*/
            else if (("normalize").equals(node.getNodeName())) {
                if (scanChildNodesForNORMALIZEParent(node)) {
                    print(NORMALIZE + node.getNodeName());
                } else {
                    print(ERROR + NORMALIZE + node.getNodeName());
                }
            }
            /////*ATTRIBUTEDEFAULT_OR_CONTENTSDEFAULT*/
            else if (("default").equals(node.getNodeName())) {
                if (scanChildNodesForATTRIBUTEDEFAULTParent(node)) {
                    print(ATTRIBUTEDEFAULT + node.getNodeName());
                } else if (scanChildNodesForCONTENTSDEFAULTParent(node)) {
                    print(CONTENTSDEFAULT + node.getNodeName());
                } else {
                    print(ERROR + ATTRIBUTEDEFAULT + COMBINE + CONTENTSDEFAULT + node.getNodeName());
                }
            }
            /////*UNIQUE*/
            else if (("unique").equals(node.getNodeName())) {
                if (scanChildNodesForNORMALIZEParent(node)) {
                    print(UNIQUE + node.getNodeName());
                } else {
                    print(ERROR + UNIQUE + node.getNodeName());
                }
            }
            /////*POINTER*/
            else if (("pointer").equals(node.getNodeName())) {
                if (scanChildNodesForNORMALIZEParent(node)) {
                    print(POINTER + node.getNodeName());
                } else {
                    print(ERROR + POINTER + node.getNodeName());
                }
            }
            /////*FIELD*/
            else if (("attributefield").equals(node.getNodeName()) | ("chardatafield").equals(node.getNodeName())) {
                if (scanChildNodesForFIELDParent(node)) {
                    print(FIELD + node.getNodeName());
                } else {
                    print(ERROR + FIELD + node.getNodeName());
                }
            }
            /////*import*/
            else if (("import").equals(node.getNodeName())) {
                print(IMPORT + node.getNodeName());
            }
            /////*prefix*/
            else if (node.getNodeName().contains(":")) {
                print(PREFIX + node.getNodeName());
            }
            /////*ERROR-NOT A DSD*/
            else {
                print(ERROR + ": " + node.getNodeName() + THIS_IS_NOT_A_VALID_DSD20_ELEMENT);
            }
        } /*else if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
            print(processingInstruction(node));
        } else if (node.getNodeType() == Node.COMMENT_NODE) {
            print(comment(node));
        } else if (node.getNodeType() == Node.DOCUMENT_NODE) {
            print(document(node));
        }*/
    }

    private static String document(Node node) {
        String tag = DOCCUMENT;
        tag += " <?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
        /*tag += "<?";
        tag += node.getNodeName() + " " + node.getTextContent();
        tag += "/>\n";*/
        return tag;
    }

    private static String comment(Node node) {
        String tag = COMMENT;
        tag += node.getTextContent();
        return tag;
    }

    private static String processingInstruction(Node node) {
        String tag = PROCESSING_INSTRUCTION;
        tag += " <?";
        tag += node.getNodeName() + " " + node.getTextContent() + node.getNodeValue();
        tag += "/>";
        return tag;
    }

    private static void clearErrorChildren() {
        ERRORCHILDREN.clear();
    }

    private static String getErrorChildren() {
        return (1 < ERRORCHILDREN.size()) ? ERRORCHILDREN.toString() + " child tags" : ERRORCHILDREN.toString() + " child tag";
    }

    private static boolean scanChildNodesForCONTENTSDEFAULTParent(Node node) {
        return ("default").equals(node.getNodeName()) && scanNodeForEMPTYParent(node);   //<default> ANYCONTENTS </default>
    }

    private static boolean scanChildNodesForATTRIBUTEDEFAULTParent(Node node) {
        return ("default").equals(node.getNodeName()) && scanNodeForEMPTYParent(node);   //<default value="VALUE" />
    }

    private static boolean scanChildNodesForNORMALIZEParent(Node node) {
        return ("normalize").equals(node.getNodeName()) && scanNodeForEMPTYParent(node);   //<normalize ( whitespace="WHITESPACE" )? ( case="CASE" )? />
    }

    private static boolean scanChildNodesForFIELDParent(Node node) {
        boolean FIELDParent = true;
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                if (("attributefield").equals(node.getNodeName())) { //<attributefield name="PANAME" ( type="ATTRTYPE" )? > BOOLEXP? </attributefield>
                    FIELDParent &= scanNodeForBOOLEXP(child);
                } else if (("chardatafield").equals(node.getNodeName())) { //<chardatafield ( type="ATTRTYPE" )? > BOOLEXP? </chardatafield>
                    FIELDParent &= scanNodeForBOOLEXP(child);
                }
            }
        }
        return FIELDParent;
    }

    private static boolean scanChildNodesForDEFINITIONParent(Node node) {
        boolean DEFINITIONParent = true;
        int ELEMENT_NODE_Count = 0;
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                ELEMENT_NODE_Count++;
                if (("rule").equals(node.getNodeName())) {  //<rule id="PENAME"> RULE* </rule>
                    boolean exp = scanNodeForRULE(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    DEFINITIONParent &= exp;
                } else if (("contenttype").equals(node.getNodeName())) {  //<contenttype id="PENAME"> REGEXP </contenttype>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForREGEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    DEFINITIONParent &= exp;
                } else if (("stringtype").equals(node.getNodeName())) {  //<stringtype id="PENAME"> REGEXP </stringtype>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForREGEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    DEFINITIONParent &= exp;
                } else if (("boolexp").equals(node.getNodeName())) {  //<boolexp id="PENAME"> BOOLEXP </boolexp>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForBOOLEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    DEFINITIONParent &= exp;
                }
            }
        }
        return DEFINITIONParent;
    }

    private static boolean scanChildNodesForREGEXPParent(Node node) {
        boolean REGEXPParent = true;
        NodeList list = node.getChildNodes();
        if (("string").equals(node.getNodeName())) {                //<string ( value="VALUE" )? />
            return scanNodeForEMPTYParent(node);
        } else if (("char").equals(node.getNodeName())) {      //<char ( ( set="VALUE" )?  |  min="CHAR" max="CHAR" ) />
            return scanNodeForEMPTYParent(node);
        } else if (("stringtype").equals(node.getNodeName())) {     //<stringtype ref="PENAME"/>
            return scanNodeForEMPTYParent(node);
        } else if (("contenttype").equals(node.getNodeName())) {    //<contenttype ref="PENAME"/>
            return scanNodeForEMPTYParent(node);
        } else if (scanNodeForBOOLEXP(node)) {    //BOOLEXP
            return scanChildNodesForBOOLEXPParent(node);
        }
        int ELEMENT_NODE_Count = 0;
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                ELEMENT_NODE_Count++;
                if (("sequence").equals(node.getNodeName())) {         //<sequence> REGEXP* </sequence>
                    boolean exp = scanNodeForREGEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    REGEXPParent &= exp;
                } else if (("optional").equals(node.getNodeName())) {   //<optional> REGEXP </optional>                    REGEXPParent &= (1 == ELEMENT_NODE_Count && scanNodeForREGEXP(child));
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForREGEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    REGEXPParent &= exp;
                } else if (("complement").equals(node.getNodeName())) {  //<complement> REGEXP </complement>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForREGEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    REGEXPParent &= exp;
                } else if (("union").equals(node.getNodeName())) {//<union> REGEXP* </union>
                    boolean exp = scanNodeForREGEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    REGEXPParent &= exp;
                } else if (("intersection").equals(node.getNodeName())) {//<intersection> REGEXP* </intersection>
                    boolean exp = scanNodeForREGEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    REGEXPParent &= exp;
                } else if (("minus").equals(node.getNodeName())) {  //<minus> REGEXP REGEXP </minus>
                    if (ELEMENT_NODE_Count == 1) {
                        boolean exp = scanNodeForREGEXP(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        REGEXPParent &= exp;
                    } else if (ELEMENT_NODE_Count == 2) {
                        boolean exp = scanNodeForREGEXP(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        REGEXPParent &= exp;
                    } else if (2 < ELEMENT_NODE_Count) {
                        return false;
                    }
                } else if (("repeat").equals(node.getNodeName())) { //<repeat ( ( number="NUMERAL" )?  |  ( min="NUMERAL" )? ( max="NUMERAL" )? ) > REGEXP </repeat>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForREGEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    REGEXPParent &= exp;
                }
            }
        }
        return REGEXPParent;
    }

    private static boolean scanChildNodesForBOOLEXPParent(Node node) {
        boolean BOOLEXPParent = true;
        NodeList list = node.getChildNodes();
        if (("this").equals(node.getNodeName())) {                //<this/>
            return scanNodeForEMPTYParent(node);
        } else if (("element").equals(node.getNodeName())) {      //<element ( name="PENAME" )? />
            return scanNodeForEMPTYParent(node);
        } else if (("boolexp").equals(node.getNodeName())) {     //<boolexp ref="PENAME"/>    
            return scanNodeForEMPTYParent(node);
        } else if (("attribute").equals(node.getNodeName())) {    //<attribute ( name="PANAME" )? > REGEXP? </attribute>
            if (scanNodeForEMPTYParent(node)) {
                return true;
            } else {
                int ELEMENT_NODE_Count = 0;
                for (int i = 0; i < list.getLength(); i++) {
                    Node child = list.item(i);
                    if (child.getNodeType() == Node.ELEMENT_NODE) {
                        ELEMENT_NODE_Count++;
                        boolean exp = 1 >= ELEMENT_NODE_Count && scanNodeForREGEXP(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        BOOLEXPParent &= exp;
                    }
                }
            }
        }
        int ELEMENT_NODE_Count = 0;
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                ELEMENT_NODE_Count++;
                if (("and").equals(node.getNodeName())) {         //<and> BOOLEXP* </and>
                    boolean exp = scanNodeForBOOLEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("or").equals(node.getNodeName())) {   //<or> BOOLEXP* </or>
                    boolean exp = scanNodeForBOOLEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("not").equals(node.getNodeName())) {  //<not> BOOLEXP </not>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForBOOLEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("imply").equals(node.getNodeName())) {//<imply> BOOLEXP BOOLEXP </imply>
                    if (ELEMENT_NODE_Count == 1) {
                        boolean exp = scanNodeForBOOLEXP(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        BOOLEXPParent &= exp;
                    } else if (ELEMENT_NODE_Count == 2) {
                        boolean exp = scanNodeForBOOLEXP(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        BOOLEXPParent &= exp;
                    } else if (2 < ELEMENT_NODE_Count) {
                        return false;
                    }
                } else if (("equiv").equals(node.getNodeName())) {//<equiv> BOOLEXP* </equiv>
                    boolean exp = scanNodeForBOOLEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("one").equals(node.getNodeName())) {  //<one> BOOLEXP* </one>
                    boolean exp = scanNodeForBOOLEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("parent").equals(node.getNodeName())) { //<parent> BOOLEXP </parent>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForBOOLEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("ancestor").equals(node.getNodeName())) {//<ancestor> BOOLEXP </ancestor>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForBOOLEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("child").equals(node.getNodeName())) {  //<child> BOOLEXP </child>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForBOOLEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("descendant").equals(node.getNodeName())) { //<descendant> BOOLEXP </descendant>
                    boolean exp = (1 == ELEMENT_NODE_Count && scanNodeForBOOLEXP(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                } else if (("contents").equals(node.getNodeName())) { //<contents> REGEXP* </contents>
                    boolean exp = scanNodeForREGEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    BOOLEXPParent &= exp;
                }
            }
        }
        return BOOLEXPParent;
    }

    private static boolean scanChildNodesForATTRIBUTEDECLParent(Node node) {   // ( REGEXP  |  NORMALIZE  |  ATTRIBUTEDEFAULT )*
        boolean ATTRIBUTEDECLParent = true;
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                boolean exp = (scanNodeForREGEXP(child) | scanNodeForNORMALIZE(child) | scanNodeForATTRIBUTEDEFAULT(child));
                if (!exp) {
                    ERRORCHILDREN.add(child.getNodeName());
                }
                ATTRIBUTEDECLParent &= exp;
            }
        }
        return ATTRIBUTEDECLParent;
    }

    private static boolean scanChildNodesForDECLARATIONParent(Node node) {
        boolean DECLARATIONParent = true;
        NodeList list = node.getChildNodes();
        if (scanNodeForEMPTYParent(node)) {
            return false;
        } else if (scanNodeForATTRIBUTEDECL(node)) {    //ATTRIBUTEDECL - <attribute ( name="PANAME" )? ( type="ATTRTYPE" )? > ( REGEXP  |  NORMALIZE  |  ATTRIBUTEDEFAULT )* </attribute>
            return scanChildNodesForATTRIBUTEDECLParent(node);
        }
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                if (("contents").equals(node.getNodeName())) {   //<contents> ( REGEXP  |  NORMALIZE  |  CONTENTSDEFAULT )* </contents>
                    boolean exp = (scanNodeForREGEXP(child) | scanNodeForNORMALIZE(child) | scanNodeForCONTENTSDEFAULT(child));
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    DECLARATIONParent &= exp;
                } else if (("required").equals(node.getNodeName())) { //<required> ATTRIBUTEDECL* </required>
                    boolean exp = scanNodeForATTRIBUTEDECL(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    DECLARATIONParent &= exp;
                }
            }
        }
        return DECLARATIONParent;
    }

    private static boolean scanChildNodesForRULEParent(Node node) {
        boolean RULEParent = true;
        int ELEMENT_NODE_Count = 0;
        if (("rule").equals(node.getNodeName())) {//<rule ref="PENAME"/>
            return scanNodeForEMPTYParent(node);
        } else if (scanNodeForUNIQUE(node)) {  //UNIQUE
            return scanChildNodesForUNIQUEParent(node);
        } else if (scanNodeForPOINTER(node)) {   //POINTER
            return scanChildNodesForPOINTERParent(node);
        }
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                ELEMENT_NODE_Count++;
                if (("declare").equals(node.getNodeName())) {  //<declare> DECLARATION* </declare>
                    boolean exp = scanNodeForDECLARATION(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    RULEParent &= exp;
                } else if (("require").equals(node.getNodeName())) { //<require> BOOLEXP* </require>
                    boolean exp = scanNodeForBOOLEXP(child);
                    if (!exp) {
                        ERRORCHILDREN.add(child.getNodeName());
                    }
                    RULEParent &= exp;
                } else if (("if").equals(node.getNodeName())) {   //<if> BOOLEXP RULE* </if>
                    if (ELEMENT_NODE_Count == 1) {
                        boolean exp = scanNodeForBOOLEXP(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        RULEParent &= exp;
                    } else if (1 < ELEMENT_NODE_Count) {
                        boolean exp = scanNodeForRULE(child);
                        if (!exp) {
                            ERRORCHILDREN.add(child.getNodeName());
                        }
                        RULEParent &= exp;
                    }
                }
            }
        }
        return RULEParent;
    }

    private static boolean scanChildNodesForPOINTERParent(Node node) { //BOOLEXP? FIELD*
        boolean POINTERParent = true;
        NodeList list = node.getChildNodes();
        int ELEMENT_NODE_Count = 0;
        Node firstElement = null;
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                ELEMENT_NODE_Count++;
                if (ELEMENT_NODE_Count == 1) {
                    firstElement = child;
                    POINTERParent &= (scanNodeForBOOLEXP(firstElement) || scanNodeForFIELD(firstElement));
                } else if (1 < ELEMENT_NODE_Count) {
                    POINTERParent &= scanNodeForFIELD(child);
                }

            }
        }
        return POINTERParent;
    }

    private static boolean scanChildNodesForUNIQUEParent(Node node) { // ( BOOLEXP FIELD* | ( <select> BOOLEXP FIELD* </select> )* )
        boolean UNIQUEParent = true;
        NodeList list = node.getChildNodes();
        int ELEMENT_NODE_Count = 0;
        Node firstElement = null;
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                ELEMENT_NODE_Count++;
                if (ELEMENT_NODE_Count == 1) {
                    firstElement = child;
                    UNIQUEParent &= (scanNodeForBOOLEXP(firstElement) || ("select").equals(firstElement.getNodeName()));
                } else if (1 < ELEMENT_NODE_Count) {
                    if (scanNodeForBOOLEXP(firstElement)) {
                        UNIQUEParent &= scanNodeForFIELD(child);
                    } else if (("select").equals(firstElement.getNodeName())) {
                        UNIQUEParent &= ("select").equals(child.getNodeName());
                    }
                }
            }
        }
        return UNIQUEParent;
    }

    private static boolean scanChildNodesForSCHEMAParent(Node node) { //( RULE  |  DEFINITION  |  SCHEMA )*
        boolean SCHEMAParent = true;
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node child = list.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE) {
                boolean exp = (scanNodeForRULE(child) | scanNodeForDEFINITION(child) | ("import").equals(child.getNodeName()) | child.getNodeName().contains(":")/*|scanNodeForDEFINITION(child)*/);
                if (!exp) {
                    ERRORCHILDREN.add(child.getNodeName());
                }
                SCHEMAParent &= exp;
            }
        }
        return SCHEMAParent;
    }

    private static boolean scanNodeForEMPTYParent(Node node) {
        return (node.getChildNodes().getLength() == 0);
    }

    private static boolean scanNodeForPOINTER(Node node) {
        return (("pointer").equals(node.getNodeName()));
    }

    private static boolean scanNodeForUNIQUE(Node node) {
        return (("unique").equals(node.getNodeName()));
    }

    private static boolean scanNodeForATTRIBUTEDECL(Node node) {
        return (("attribute").equals(node.getNodeName()));
    }

    private static boolean scanNodeForCONTENTSDEFAULT(Node node) {
        return (("default").equals(node.getNodeName()) & !scanNodeForEMPTYParent(node));
    }

    private static boolean scanNodeForATTRIBUTEDEFAULT(Node node) {
        return (("default").equals(node.getNodeName()) & scanNodeForEMPTYParent(node));
    }

    private static boolean scanNodeForNORMALIZE(Node node) {
        return (("normalize").equals(node.getNodeName()));
    }

    private static boolean scanNodeForREGEXP(Node node) {
        return (("sequence").equals(node.getNodeName()) | ("optional").equals(node.getNodeName()) | ("complement").equals(node.getNodeName()) | ("union").equals(node.getNodeName()) | ("intersection").equals(node.getNodeName()) | ("minus").equals(node.getNodeName()) | ("repeat").equals(node.getNodeName()) | ("string").equals(node.getNodeName()) | ("char").equals(node.getNodeName()) | ("stringtype").equals(node.getNodeName()) | ("contenttype").equals(node.getNodeName()) | scanNodeForBOOLEXP(node));
    }

    private static boolean scanNodeForFIELD(Node node) {
        return (("attributefield").equals(node.getNodeName()) | ("chardatafield").equals(node.getNodeName()));
    }

    private static boolean scanNodeForBOOLEXP(Node node) {
        return (("and").equals(node.getNodeName()) | ("or").equals(node.getNodeName()) | ("not").equals(node.getNodeName()) | ("imply").equals(node.getNodeName()) | ("equiv").equals(node.getNodeName()) | ("one").equals(node.getNodeName()) | ("parent").equals(node.getNodeName()) | ("ancestor").equals(node.getNodeName()) | ("child").equals(node.getNodeName()) | ("node").equals(node.getNodeName()) | ("descendant").equals(node.getNodeName()) | ("this").equals(node.getNodeName()) | ("element").equals(node.getNodeName()) | ("attribute").equals(node.getNodeName()) | ("contents").equals(node.getNodeName()) | ("boolexp").equals(node.getNodeName()));
    }

    private static boolean scanNodeForDECLARATION(Node node) {
        return (("attribute").equals(node.getNodeName()) | ("required").equals(node.getNodeName()) | ("contents").equals(node.getNodeName()));
    }

    private static boolean scanNodeForDEFINITION(Node node) {
        ////*DEFINITION*//
        return (("rule").equals(node.getNodeName()) | ("contenttype").equals(node.getNodeName()) | ("stringtype").equals(node.getNodeName()) | ("boolexp").equals(node.getNodeName()));
    }

    private static boolean scanNodeForRULE(Node node) {
        ////*RULE*/
        return ("declare").equals(node.getNodeName()) | ("require").equals(node.getNodeName()) | ("if").equals(node.getNodeName()) | ("rule").equals(node.getNodeName()) | ("unique").equals(node.getNodeName()) | ("pointer").equals(node.getNodeName());
    }


    private static void print(String string) {
        for (int i = 0; i < currentLevel; i++) {
            System.out.print(INDENT);
        }
        System.out.println(string);

        for (int i = 0; i < currentLevel; i++) {
            try {
                fos.write(INDENT);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        try {
            fos.write(string);
            fos.write("\r\n");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
